[POWERPC][XEN] Set up an RTAS node for dom0 and proxy methods
authorJimi Xenidis <jimix@watson.ibm.com>
Tue, 5 Jun 2007 20:09:58 +0000 (16:09 -0400)
committerJimi Xenidis <jimix@watson.ibm.com>
Tue, 5 Jun 2007 20:09:58 +0000 (16:09 -0400)
If real FW supplies an RTAS layer then pass thru the methods Xen will
proxy for Dom0.
 - NVRAM read/write methods work, see  nvsetenv(8) or nvram(8).
 - Firmware flash methods exist, but currently "chicken switched" off
   for further testing.

Signed-off-by: Jimi Xenidis <jimix@watson.ibm.com>
xen/arch/powerpc/Makefile
xen/arch/powerpc/ofd_fixup.c
xen/arch/powerpc/oftree.h
xen/arch/powerpc/rtas.c
xen/arch/powerpc/rtas.h
xen/arch/powerpc/rtas_flash.c [new file with mode: 0644]
xen/arch/powerpc/rtas_nvram.c [new file with mode: 0644]

index 04613537075d80ec9f4ec104dfc139ecaf0a7486..4e00c1579795394cc6a58ee6af88af2823ff0e4f 100644 (file)
@@ -35,6 +35,8 @@ obj-y += ofd_fixup_memory.o
 obj-y += physdev.o
 obj-y += platform.o
 obj-y += rtas.o
+obj-y += rtas_nvram.o
+obj-y += rtas_flash.o
 obj-y += setup.o
 obj-y += shadow.o
 obj-y += smp.o
index 2d2ff112447bccbac56d2ba540f82ae34ad64a07..6deb3c888c46e65fdc30d21a2659ed8635381eb9 100644 (file)
@@ -27,8 +27,6 @@
 #include "oftree.h"
 #include "rtas.h"
 
-#undef RTAS
-
 ofdn_t ofd_boot_cpu;
 
 #ifdef PAPR_VTERM
@@ -307,26 +305,6 @@ static ofdn_t ofd_chosen_props(void *m, const char *cmdline)
     return n;
 }
 
-#ifdef RTAS
-static ofdn_t ofd_rtas_props(void *m)
-{
-    static const char path[] = "/rtas";
-    static const char hypertas[] = "dummy";
-    ofdn_t p;
-    ofdn_t n;
-
-    /* just enough to make linux think its on LPAR */
-
-    p = ofd_node_find(m, "/");
-
-    n = ofd_node_add(m, p, path, sizeof(path));
-    ofd_prop_add(m, n, "name", &path[1], sizeof (path) - 1);
-    ofd_prop_add(m, n, "ibm,hypertas-functions", hypertas, sizeof (hypertas));
-
-    return n;
-}
-#endif
-
 static ofdn_t ofd_xen_props(void *m, struct domain *d, ulong shared_info)
 {
     ofdn_t n;
@@ -382,8 +360,8 @@ static ofdn_t ofd_xen_props(void *m, struct domain *d, ulong shared_info)
     return n;
 }
 
-int ofd_dom0_fixup(struct domain *d, ulong mem, const char *cmdline,
-                   ulong shared_info)
+ulong ofd_dom0_fixup(struct domain *d, ulong mem, const char *cmdline,
+                     ulong shared_info)
 {
     void *m;
     const ofdn_t n = OFD_ROOT;
@@ -423,11 +401,8 @@ int ofd_dom0_fixup(struct domain *d, ulong mem, const char *cmdline,
     printk("Remove original /rtas\n");
     ofd_prune_path(m, "/rtas");
 
-#ifdef RTAS
-    printk("Create a new RTAS with just enough stuff to convince "
-           "Linux that its on LPAR\n");
-    ofd_rtas_props(m);
-#endif
+    rtas_proxy_init(m);
+
 #ifdef FIX_COMPAT 
     const char compat[] = "Hypervisor,Maple";
     r = ofd_prop_add(m, n, "compatible", compat, sizeof (compat));
@@ -446,5 +421,5 @@ int ofd_dom0_fixup(struct domain *d, ulong mem, const char *cmdline,
 #ifdef DEBUG
     ofd_walk(m, __func__, OFD_ROOT, ofd_dump_props, OFD_DUMP_ALL);
 #endif
-    return 1;
+    return ofd_size(m);
 }
index debc4634b6d9df838f48390c59ce049cdac5694c..3df6a8ff0d40612da608d68f7871ba254f764a39 100644 (file)
@@ -28,8 +28,8 @@ extern ulong oftree_len;
 extern ulong oftree_end;
 extern ofdn_t ofd_boot_cpu;
 
-extern int ofd_dom0_fixup(struct domain *d, ulong mem, const char *cmdline,
-                          ulong shared_info);
+extern ulong ofd_dom0_fixup(struct domain *d, ulong mem, const char *cmdline,
+                            ulong shared_info);
 extern void ofd_memory_props(void *m, struct domain *d);
 
 extern int firmware_image_start[0];
index 6b1451eff31a4dc3a51c0f8f4f8b81a50689718c..4584444ee0a3df7e10d8023540088e3dd187bd6e 100644 (file)
@@ -13,7 +13,7 @@
  * along with this program; if not, write to the Free Software
  * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  *
- * Copyright (C) IBM Corp. 2006
+ * Copyright (C) IBM Corp. 2006, 2007
  *
  * Authors: Jimi Xenidis <jimix@watson.ibm.com>
  */
 #include <xen/init.h>
 #include <xen/lib.h>
 #include <xen/errno.h>
+#include <xen/sched.h>
 #include "of-devtree.h"
 #include "rtas.h"
 
-static int rtas_halt_token = -1;
-static int rtas_reboot_token = -1;
 int rtas_entry;
 unsigned long rtas_msr;
 unsigned long rtas_base;
 unsigned long rtas_end;
 
-struct rtas_args {
-    int ra_token;
-    int ra_nargs;
-    int ra_nrets;
-    int ra_args[10];
-} __attribute__ ((aligned(8)));
-
-static int rtas_call(struct rtas_args *r)
+int rtas_call(void *r)
 {
     if (rtas_entry == 0)
         return -ENOSYS;
@@ -47,11 +39,51 @@ static int rtas_call(struct rtas_args *r)
     return prom_call(r, rtas_base, rtas_entry, rtas_msr);
 }
 
+/* rtas always uses physical address */
+void *rtas_remote_addr(ulong addr, ulong length)
+{
+    struct vcpu *v = get_current();
+    struct domain *d = v->domain;
+    ulong mfn;
+    ulong mfn_end;
+
+    mfn = gmfn_to_mfn(d, addr >> PAGE_SHIFT);
+    if (mfn == INVALID_MFN)
+        return NULL;
+
+    /* a little paranoid since almost everyone will pass us page
+     * bounded thingies, but just in case */
+    mfn_end = gmfn_to_mfn(d, (addr + length) >> PAGE_SHIFT);
+    if (mfn_end == INVALID_MFN)
+        return NULL;
+
+    return (void *)((mfn << PAGE_SHIFT) | (addr & (PAGE_SIZE - 1)));
+}
+
+/* these do not proxy */
+#define RTAS_HALT 0
+#define RTAS_REBOOT 1
+
+struct rtas_token rt_power_off = { .name = "power-off", .token = -1, };
+struct rtas_token rt_system_reboot = { .name = "system-reboot", .token = -1};
+
+static struct rtas_token *tokens[] = {
+    /* these do not proxy */
+    [RTAS_HALT] = &rt_power_off,
+    [RTAS_REBOOT] = &rt_system_reboot,
+    &rt_nvram_store,
+    &rt_nvram_fetch,
+    &rt_manage_flash,
+    &rt_validate_flash,
+    &rt_update_reboot_flash
+};
+
+static int rtas_proxy;
+
 int __init rtas_init(void *m)
 {
-    static const char halt[] = "power-off";
-    static const char reboot[] = "system-reboot";
     ofdn_t n;
+    int i;
 
     if (rtas_entry == 0)
         return -ENOSYS;
@@ -60,22 +92,87 @@ int __init rtas_init(void *m)
     if (n <= 0)
         return -ENOSYS;
 
-    ofd_getprop(m, n, halt,
-                &rtas_halt_token, sizeof (rtas_halt_token));
-    ofd_getprop(m, n, reboot,
-                &rtas_reboot_token, sizeof (rtas_reboot_token));
+    for (i = 0; i < ARRAY_SIZE(tokens); i++) {
+        ofd_getprop(m, n, tokens[i]->name,
+                    &tokens[i]->token, sizeof (tokens[i]->token));
+        if (!rtas_proxy && tokens[i]->proxy && tokens[i]->token != -1)
+            rtas_proxy = 1;
+    }
     return 1;
 }
 
+int rtas_proxy_init(void *m)
+{
+    static const char path[] = "/rtas";
+    ofdn_t p;
+    ofdn_t n;
+    int i;
+
+    if (!rtas_proxy)
+        return -1;
+
+    printk("Create a new /rtas with tokens Xen is willing to proxy\n");
+
+    p = ofd_node_find(m, "/");
+
+    n = ofd_node_add(m, p, path, sizeof(path));
+    ofd_prop_add(m, n, "name", &path[1], sizeof (path) - 1);
+
+    /* and the tokens for proxy */
+    for (i = 0; i < ARRAY_SIZE(tokens); i++) {
+        if (tokens[i]->proxy && tokens[i]->token != -1)
+            ofd_prop_add(m, n, tokens[i]->name, &i, sizeof (i));
+    }
+    return n;
+}
+
+int do_rtas_proxy(ulong arg)
+{
+    struct rtas_args *r;
+    unsigned i;
+    int token;
+    ulong sz;
+
+    if (!IS_PRIV(current->domain))
+        return -EPERM;
+    if (!rtas_proxy)
+        return -ENOSYS;
+
+    /* has to be at least 5 words */
+    sz = (3 + 1 + 1) * sizeof (int);
+    r = rtas_remote_addr(arg, sz);
+    if (r == NULL) {
+        /* this is about all we can do at this point */
+        return -1;
+    }
+    /* make sure we can deal with everything */
+    sz = (3 + r->ra_nargs + r->ra_nrets) * sizeof (int);
+    if (rtas_remote_addr(arg, sz) == NULL) {
+        r->ra_args[r->ra_nargs] = RTAS_HW;
+        return -1;
+    }
+
+    i = r->ra_token;
+    token = tokens[i]->token;
+
+    if (i < ARRAY_SIZE(tokens) &&
+        tokens[i]->proxy != NULL &&
+        token != -1)
+        return tokens[i]->proxy(token, r);
+
+    return -1;
+}
+
 int
 rtas_halt(void)
 {
     struct rtas_args r;
+    int token = tokens[RTAS_HALT]->token;
 
-    if (rtas_halt_token == -1)
+    if (token == -1)
         return -1;
 
-    r.ra_token = rtas_halt_token;
+    r.ra_token = token;
     r.ra_nargs = 2;
     r.ra_nrets = 1;
     r.ra_args[0] = 0;
@@ -89,10 +186,12 @@ rtas_reboot(void)
 {
     struct rtas_args r;
 
-    if (rtas_reboot_token == -1)
-        return -ENOSYS;
+    int token = tokens[RTAS_REBOOT]->token;
+
+    if (token == -1)
+        return -1;
 
-    r.ra_token = rtas_reboot_token;
+    r.ra_token = token;
     r.ra_nargs = 2;
     r.ra_nrets = 1;
     r.ra_args[0] = 0;
index 2b78779233a1b58f3f37707f55cdd32188def977..1718530b747790857c0ba6510cd8f2c675005c5b 100644 (file)
@@ -26,9 +26,40 @@ extern unsigned long rtas_msr;
 extern unsigned long rtas_base;
 extern unsigned long rtas_end;
 
+struct rtas_args {
+    int ra_token;
+    int ra_nargs;
+    int ra_nrets;
+    int ra_args[10];
+} __attribute__ ((aligned(8)));
+
+struct rtas_token {
+    char *name;
+    int (*proxy)(int token, struct rtas_args *r);
+    int token;
+};
+
+extern struct rtas_token rt_power_off;
+extern struct rtas_token rt_system_reboot;
+extern struct rtas_token rt_nvram_fetch;
+extern struct rtas_token rt_nvram_store;
+extern struct rtas_token rt_manage_flash;
+extern struct rtas_token rt_validate_flash;
+extern struct rtas_token rt_update_reboot_flash;
+
+/* RTAS errors */
+#define RTAS_HW -1
+#define RTAS_BUSY -2
+#define RTAS_PARAMETER -3
+
+
 extern int prom_call(void *arg, unsigned base,
                      unsigned long func, unsigned long msr);
 extern int rtas_init(void *);
 extern int rtas_halt(void);
 extern int rtas_reboot(void);
+extern int rtas_proxy_init(void *m);
+extern int do_rtas_proxy(ulong arg);
+extern void *rtas_remote_addr(ulong addr, ulong length);
+extern int rtas_call(void *r);
 #endif
diff --git a/xen/arch/powerpc/rtas_flash.c b/xen/arch/powerpc/rtas_flash.c
new file mode 100644 (file)
index 0000000..c48898b
--- /dev/null
@@ -0,0 +1,182 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ * Copyright (C) IBM Corp. 2007
+ *
+ * Authors: Jimi Xenidis <jimix@watson.ibm.com>
+ */
+
+#include <xen/config.h>
+#include <xen/init.h>
+#include <xen/lib.h>
+#include <xen/sched.h>
+#include "rtas.h"
+
+static int rtas_manage_flash(int token, struct rtas_args *ra)
+{
+    struct rtas_args r;
+    ulong sz = (3 + ra->ra_nargs + ra->ra_nrets) * sizeof (int);
+    int ret;
+
+    if (ra->ra_nargs != 1 || ra->ra_nrets != 1) {
+        ra->ra_args[ra->ra_nargs] = RTAS_PARAMETER;
+        return -1;
+    }
+    memcpy(&r, ra, sz);
+    r.ra_token = token;
+
+    ret = rtas_call(&r);
+    ra->ra_args[ra->ra_nargs] = r.ra_args[r.ra_nargs];
+    return ret;
+}
+struct rtas_token rt_manage_flash = {
+    .name = "ibm,manage-flash-image",
+    .proxy = rtas_manage_flash,
+    .token = -1
+};
+
+static int rtas_validate_flash(int token, struct rtas_args *ra)
+{
+    ulong length = ra->ra_args[1];
+    char *buffer;
+    char *local;
+    struct rtas_args r;
+    ulong sz = (3 + ra->ra_nargs + ra->ra_nrets) * sizeof (int);
+    int ret;
+
+    if (ra->ra_nargs != 2 || ra->ra_nrets != 2) {
+        ra->ra_args[ra->ra_nargs] = RTAS_PARAMETER;
+        return -1;
+    }
+
+    /* the original pointer can be in memory that is too high so we
+     * need to do it locally */
+    buffer = rtas_remote_addr(ra->ra_args[0], length);
+    if (buffer == NULL) {
+        ra->ra_args[ra->ra_nargs] = RTAS_PARAMETER;
+        return -1;
+    }
+        
+    local = xmalloc_bytes(length);
+    if (local == NULL) {
+        printk("%s: could not allocate local buffer size: 0x%lx\n",
+               __func__, length);
+        ra->ra_args[ra->ra_nargs] = RTAS_HW;
+        return -1;
+    }
+    /* RTAS is 32bits so we need to make sure that that local
+     * buffer is in that range */
+    BUG_ON(((ulong)local + length) & ~0xffffffffUL);
+
+    /* copy the remote buffer to the local one */
+    memcpy(local, buffer, length);
+
+    memcpy(&r, ra, sz);
+    r.ra_token = token;
+    r.ra_args[0] = (unsigned)(ulong)local;
+    ret = rtas_call(&r);
+    ra->ra_args[ra->ra_nargs] = r.ra_args[r.ra_nargs];
+    ra->ra_args[ra->ra_nargs + 1] = r.ra_args[r.ra_nargs + 1];
+    xfree(local);
+    return ret;
+}
+
+struct rtas_token rt_validate_flash = {
+    .name = "ibm,validate-flash-image",
+    .proxy = rtas_validate_flash,
+    .token = -1
+};
+
+/* flash data structs */
+struct flash_block {
+    u64 addr;
+    u64 length;
+};
+struct flash_block_list {
+    struct {
+        u64 ver:8;
+        u64 bytes:56;
+    } header;
+    u64 *next;
+    struct flash_block blocks[0];
+};
+
+static int safe_to_flash;
+static int rtas_update_reboot_flash(int token, struct rtas_args *ra)
+{
+    struct rtas_args r;
+    ulong sz = (3 + ra->ra_nargs + ra->ra_nrets) * sizeof (int);
+    int ret;
+    void *local;
+    struct flash_block_list *l;
+    ulong blocks;
+    
+    if (ra->ra_nargs != 1 || ra->ra_nrets != 1) {
+        ra->ra_args[ra->ra_nargs] = RTAS_PARAMETER;
+        return -1;
+    }
+
+    if (!safe_to_flash) {
+        printk("%s: this has not been fully tested yet\n", __func__);
+        ra->ra_args[ra->ra_nargs] = RTAS_HW;
+        return -1;
+    }
+
+    /* we only need to relocate the first block address to 4G, for now
+     * lets just bug on that */
+    local = rtas_remote_addr(ra->ra_args[0], 16);
+    BUG_ON((ulong)local & ~0xffffffffUL);
+
+    /* now we run through the block list and translate base addresses */
+    l = (struct flash_block_list *)local;
+
+    /* header and next count as one block */
+    blocks = (l->header.bytes / sizeof (struct flash_block)) - 1;
+    if (blocks == 0) {
+        ra->ra_args[ra->ra_nargs] = RTAS_PARAMETER;
+        return -1;
+    }
+
+    /* go thru the block lists */
+    do {
+        int i = 0;
+
+        /* go thru the block in the list */
+        for (i = 0; i < blocks; i++) {
+            void *addr;
+
+            addr = rtas_remote_addr(l->blocks[i].addr, l->blocks[i].length);
+            BUG_ON(addr == NULL);
+            l->blocks[i].addr = (u64)addr;
+        }
+        l = (struct flash_block_list *)l->next;
+    } while (l != NULL);
+
+    memcpy(&r, ra, sz);
+    r.ra_token = token;
+
+    /* this arguement is a pointer to a block list */
+    r.ra_args[0] = (unsigned)(ulong)local;
+
+    ret = rtas_call(&r);
+    ra->ra_args[ra->ra_nargs] = r.ra_args[r.ra_nargs];
+    return ret;
+}
+
+struct rtas_token rt_update_reboot_flash = {
+    .name = "ibm,update-flash-64-and-reboot",
+    .proxy = rtas_update_reboot_flash,
+    .token = -1
+};
diff --git a/xen/arch/powerpc/rtas_nvram.c b/xen/arch/powerpc/rtas_nvram.c
new file mode 100644 (file)
index 0000000..ec9c57f
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ * Copyright (C) IBM Corp. 2007
+ *
+ * Authors: Jimi Xenidis <jimix@watson.ibm.com>
+ */
+
+#include <xen/config.h>
+#include <xen/init.h>
+#include <xen/lib.h>
+#include <xen/sched.h>
+#include "rtas.h"
+
+static int rtas_nvram_store(int token, struct rtas_args *ra)
+{
+    ulong length = ra->ra_args[2];
+    char *buffer;
+    char *local;
+    struct rtas_args r;
+    ulong sz = (3 + ra->ra_nargs + ra->ra_nrets) * sizeof (int);
+    int ret;
+
+    if (ra->ra_nargs != 3 || ra->ra_nrets != 2) {
+        ra->ra_args[ra->ra_nargs] = RTAS_PARAMETER;
+        return -1;
+    }
+
+    /* the original pointer can be in memory that is too high so we
+     * need to do it locally */
+    buffer = rtas_remote_addr(ra->ra_args[1], length);
+    if (buffer == NULL) {
+        ra->ra_args[ra->ra_nargs] = RTAS_PARAMETER;
+        return -1;
+    }
+        
+    local = xmalloc_bytes(length);
+    if (local == NULL) {
+        printk("%s: could not allocate local buffer size: 0x%lx\n",
+               __func__, length);
+        ra->ra_args[ra->ra_nargs] = RTAS_HW;
+        return -1;
+    }
+    /* RTAS is 32bits so we need to make sure that that local
+     * buffer is in that range */
+    BUG_ON(((ulong)local + length) & ~0xffffffffUL);
+
+    /* copy the remote buffer to the local one */
+    memcpy(local, buffer, length);
+
+    memcpy(&r, ra, sz);
+    r.ra_token = token;
+    r.ra_args[1] = (unsigned)(ulong)local;
+
+    ret = rtas_call(&r);
+    ra->ra_args[ra->ra_nargs] = r.ra_args[r.ra_nargs];
+    ra->ra_args[ra->ra_nargs + 1] = r.ra_args[r.ra_nargs + 1];
+    xfree(local);
+    return ret;
+}
+
+struct rtas_token rt_nvram_store = {
+    .name = "nvram-store",
+    .proxy = rtas_nvram_store,
+    .token = -1
+};
+
+static int rtas_nvram_fetch(int token, struct rtas_args *ra)
+{
+    ulong length = ra->ra_args[2];
+    char *buffer;
+    char *local;
+    struct rtas_args r;
+    ulong sz = (3 + ra->ra_nargs + ra->ra_nrets) * sizeof (int);
+    int ret;
+
+    if (ra->ra_nargs != 3 || ra->ra_nrets != 2) {
+        ra->ra_args[ra->ra_nargs] = RTAS_PARAMETER;
+        return -1;
+    }
+    /* the original pointer can be in ememory that is too high so
+     * we need to do it locally */
+    buffer = rtas_remote_addr(ra->ra_args[1], length);
+
+    local = xmalloc_bytes(length);
+    if (local == NULL) {
+        printk("%s: could not allocate local buffer size: 0x%lx\n",
+               __func__, length);
+        ra->ra_args[ra->ra_nargs] = RTAS_HW;
+        return -1;
+    }
+    /* RTAS is 32bits so we need to make sure that that local
+     * buffer is in that range */
+    BUG_ON(((ulong)local + length) & ~0xffffffffUL);
+
+    memcpy(&r, ra, sz);
+    r.ra_token = token;
+    r.ra_args[1] = (unsigned)(ulong)local;
+
+    ret = rtas_call(&r);
+    ra->ra_args[ra->ra_nargs] = r.ra_args[r.ra_nargs];
+    ra->ra_args[ra->ra_nargs + 1] = r.ra_args[r.ra_nargs + 1];
+    if (r.ra_args[r.ra_nargs] >= 0) {
+        /* copy from local to remote */
+        sz = r.ra_args[r.ra_nargs + 1];
+        memcpy(buffer, local, sz);
+    }
+    xfree(local);
+    return ret;
+}
+
+struct rtas_token rt_nvram_fetch = {
+    .name = "nvram-fetch",
+    .proxy = rtas_nvram_fetch,
+    .token = -1
+};
+